package server

import (
	"go.uber.org/zap"
	"gse/transport/udp/connection"
	"net"
	"sync"
)

type ConnectionManagerConfig struct {
	*connection.UDPConnectionConfig
	Logger *zap.Logger
}
type ConnectionManager struct {
	connections *sync.Map
	stopping    chan int8
	logger      *zap.Logger
	cfg         *ConnectionManagerConfig
	udpSrvConn  *net.UDPConn
}

func NewConnectionManager(cfg *ConnectionManagerConfig, udpSrvConn *net.UDPConn) *ConnectionManager {
	m := &ConnectionManager{
		connections: new(sync.Map),
		stopping:    make(chan int8),
		cfg:         cfg,
		udpSrvConn:  udpSrvConn,
	}
	if m.cfg.Logger != nil {
		m.logger, _ = zap.NewDevelopment()
	} else {
		m.logger = m.cfg.Logger
	}
	return m
}

// GetConnection gets an existing connection or create it if not exists.
func (m *ConnectionManager) GetConnection(remoteAddr net.Addr) (conn *connection.Connection, created bool, err error) {
	if connI, exists := m.connections.Load(remoteAddr.String()); exists {
		return connI.(*connection.Connection), false, nil
	} else {
		conn, err := connection.NewIncomingConnection(remoteAddr, m.udpSrvConn)
		if err != nil {
			return nil, false, err
		}
		if connI, exists := m.connections.LoadOrStore(remoteAddr.String(), conn); exists {
			return connI.(*connection.Connection), false, nil
		} else {
			conn.SetConfig(m.cfg.UDPConnectionConfig)
			conn.OnClose = m.onConnectionClose
			return conn, true, nil
		}
	}
}

func (m *ConnectionManager) onConnectionClose(conn *connection.Connection) {
	m.logger.Debug("remove closed connection:", zap.String("remote_addr", conn.RemoteAddr().String()))
	m.connections.Delete(conn.RemoteAddr().String())
}
